#include "ae.h"

#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <bits/types/struct_timeval.h>

#include "ae_epoll.c"

aeEventLoop *aeCreateEventLoop(int setsize) {
    aeEventLoop *eventLoop;

    // 创建事件循环，如果创建失败，跳到错误处理逻辑
    if ((eventLoop = malloc(sizeof(aeEventLoop))) == NULL) goto err;

    // 初始化事件循环的成员
    eventLoop->setsize = setsize;
    eventLoop->maxfd = -1;
    eventLoop->events = malloc(sizeof(aeFileEvent) * setsize);
    eventLoop->fired = malloc(sizeof(struct aeFiredEvent) * setsize);
    for (int i = 0; i < setsize; ++i)
        eventLoop->events[i].mask = AE_NONE;
    
    // 具体所使用的IO多路复用模型的初始化操作
    if (aeApiCreate(eventLoop) == AE_ERR) goto err;

    return eventLoop;
err:
    if (eventLoop) {
        free(eventLoop->events);
        free(eventLoop);
    }
    return NULL;
}

int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
    aeFileProc *proc, void *clientData) {
    if (fd > eventLoop->setsize) {
        errno = ERANGE;
        return AE_ERR;
    }

    aeFileEvent *fe = &eventLoop->events[fd];
    if (aeApiAddEvent(eventLoop, fd, mask) == -1)
        return AE_ERR;
    fe->mask |= mask;
    if (mask & AE_READABLE) fe->rFileProc = proc;
    if (mask & AE_WRITABLE) fe->wFileProc = proc;
    fe->clientData = clientData;
    if (fd > eventLoop->maxfd)
        eventLoop->maxfd = fd;
    return AE_OK;
}

int aeProcessEvents(aeEventLoop *eventLoop, int flags) {
    int processed = 0, numevents;
    int j;

    struct timeval tv;
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    numevents = aeApiPoll(eventLoop, &tv);
    for (j = 0; j < numevents; j++) {
        int fd = eventLoop->fired[j].fd;
        aeFileEvent *fe = eventLoop->events + fd;
        int mask = eventLoop->fired[j].mask;
        int fired = 0;

        if (fe->mask && mask & AE_READABLE) {
            fe->rFileProc(eventLoop, fd, fe->clientData, mask);
            fired++;
        }

        if (fe->mask && mask & AE_WRITABLE) {
            fe->wFileProc(eventLoop, fd, fe->clientData, mask);
            fired++;
        }

        processed++;
    }

    return processed;
}

void aeMain(aeEventLoop *eventLoop) {
    while (1) {
        aeProcessEvents(eventLoop, 0);
    }
}